import Datastore from 'lowdb'
import FileSync from 'lowdb/adapters/FileSync'
import path from 'path'
import fs from 'fs-extra'
import { app, remote, dialog } from 'electron'
import moment from 'moment'
import xlsx from 'xlsx'
import { utils } from 'xlsx'
import store from '@/store';
import { parse, stringify } from 'zipson';
import _ from 'lodash';
import AutoSaverConfig from '@/models/AutoSaverConfig';
import FtpConfig from '@/models/FtpConfig'
import { AutoSaver } from './autoSaver';
import { FtpUtil } from './ftpUtil'
const APP = process.type === 'renderer' ? remote.app : app;
const DIALOG = process.type === 'renderer' ? remote.dialog : dialog;
const APP_PATH = path.join(APP.getPath('documents'), '/chenghuangmiao')
const IMAGE_PATH = APP_PATH + '\\images\\';
const DEFAULT_DB = {
    user: [],
    order: [],
    product: [],
    updateDate: moment().valueOf(),
    autoSaveConfig: new AutoSaverConfig(undefined),
    adminPassword: '123456'
}
console.log(IMAGE_PATH);
if (!fs.pathExistsSync(APP_PATH)) {
    fs.mkdirSync(APP_PATH);
}
if (!fs.pathExistsSync(IMAGE_PATH)) {
    fs.mkdirSync(IMAGE_PATH);
}
if (!fs.pathExistsSync(DEFAULT_DB.autoSaveConfig.savePath)) {
    fs.mkdirSync(DEFAULT_DB.autoSaveConfig.savePath);
}

class MyStorage {
    private adapter: any;
    private db: any;
    private autoSaver: AutoSaver | null = null;
    private lastImageSelectedPath: string = '';

    constructor() {
        try {
            this.adapter = new FileSync(path.join(APP_PATH + '/data.lowdb'), {
                serialize: (data) => stringify(data),
                deserialize: (data) => parse(data)
            });
            this.db = Datastore(this.adapter);
            if (!this.db.get('user').value()) {
                this.db.set('user', DEFAULT_DB.user).write();
            }
            if (!this.db.get('product').value()) {
                this.db.set('product', DEFAULT_DB.product).write();
            }
            if (!this.db.get('order').value()) {
                this.db.set('order', DEFAULT_DB.order).write();
            }
            if (!this.db.get('autoSaveConfig').value()) {
                this.db.set('autoSaveConfig', DEFAULT_DB.autoSaveConfig).write();
            }
            if (!this.db.get('adminPassword').value()) {
                this.db.set('adminPassword', DEFAULT_DB.adminPassword).write();
            }
            if (!this.db.get('updateDate').value()) {
                this.db.set('updateDate', DEFAULT_DB.updateDate).write();
            }
            console.log(this.db.value());
            let unixTime = this.db.get('updateDate').value();
            let version = moment(unixTime).format('YYYYMMDDHHmmss')
            store.commit('setDbVersion', version);
            this.startAutoSaver();
        } catch (e) {
            throw new Error('数据库初始化失败' + (e.message || ''))
        }
    }

    public get(key: string) {
        return new Promise((resolve, reject) => {
            try {
                resolve(this.db.get(key).value())
            } catch (err) {
                return reject(err);
            }
        })
    }
    public set(key: string, value: any) {
        return new Promise((resolve, reject) => {
            try {
                const result = this.db.set(key, value).write();
                this.db.set('updateDate', moment().valueOf()).write();
                resolve(result);
            } catch (err) {
                return reject(err);
            }
        })
    }

    public find(key: string, value: object) { // 查询单一结果,key为表名,value为查询参数(键值对)
        return new Promise((resolve, reject) => {
            try {
                resolve(this.db.get(key).find(value).value())
            } catch (err) {
                return reject(err)
            }
        })
    }
    public filter(key: string, value: any = null) { // 查询多条
        return new Promise((resolve, reject) => {
            try {
                resolve(this.db.get(key).filter(value).value())
            } catch (err) {
                return reject(err)
            }
        })
    }
    public insert(key: string, value?: object) { // 插入
        return new Promise((resolve, reject) => {
            try {
                let result = this.db.get(key).push(value).write();
                this.db.set('updateDate', moment().valueOf()).write();
                resolve(result)
            } catch (err) {
                return reject(err)
            }
        })
    }
    public delete(key: string, value: any) { // 删除
        return new Promise((resolve, reject) => {
            try {
                let result = this.db.get(key).remove(value).write();
                this.db.set('updateDate', moment().valueOf()).write();
                resolve(result)
            } catch (err) {
                return reject(err)
            }
        })
    }
    public update(key: string, value: any) {
        return new Promise((resolve, reject) => {
            try {
                let result = this.db.get(key).find({ id: value.id }).assign(value).write();
                this.db.set('updateDate', moment().valueOf()).write();
                resolve(result);
            } catch (err) {
                return reject(err);
            }
        })
    }
    public reset() {
        return new Promise((resolve, reject) => {
            let currentAdminPassword = this.db.get('adminPassword').value();
            if (!currentAdminPassword && currentAdminPassword === '') {
                currentAdminPassword = DEFAULT_DB.adminPassword;
            }
            this.db.setState(DEFAULT_DB).write();
            this.db.set('adminPassword', currentAdminPassword).write();
            resolve();
        })
    }
    public exportData() {
        return new Promise((resolve, reject) => {
            const saveOptions = {
                defaultPath: APP.getPath('documents') + '/data_all.lowdb',
                filters: [
                    { name: '数据库文件', extensions: ['lowdb'] }
                ]
            }
            let jsonString = stringify(this.db.value());
            DIALOG.showSaveDialog(saveOptions, (selectedPath) => {
                if (selectedPath) {
                    fs.writeFile(selectedPath, jsonString, 'utf-8', (err) => {
                        if (err) {
                            reject(err)
                        } else {
                            resolve();
                        }
                    });
                } else {
                    reject(new Error('canceled'));
                }
            })
        })
    }
    public exportDataByKey(key: string) {
        return new Promise((resolve, reject) => {
            const saveOptions = {
                defaultPath: APP.getPath('documents') + '/data_' + key + 's.lowdb',
                filters: [
                    { name: '数据库文件', extensions: ['lowdb'] }
                ]
            }
            let dbObj: any = {};
            dbObj[key] = this.db.get(key).value();
            let jsonString = stringify(dbObj);
            DIALOG.showSaveDialog(saveOptions, (selectedPath) => {
                if (selectedPath) {
                    fs.writeFile(selectedPath, jsonString, 'utf-8', (err) => {
                        if (err) {
                            reject(err)
                        } else {
                            resolve();
                        }
                    });
                } else {
                    reject(new Error('canceled'));
                }
            })
        })
    }
    public importData() {
        const openOptions = {
            defaultPath: APP.getPath('documents') + '/data_all.lowdb',
            filters: [
                { name: '数据库文件', extensions: ['lowdb'] }
            ]
        }
        return new Promise((resolve, reject) => {
            DIALOG.showOpenDialog(openOptions, (selectedPath) => {
                if (selectedPath) {
                    let data = fs.readFileSync(selectedPath[0], 'utf-8');
                    let dataObject = parse(data);
                    this.db.set('user', dataObject.user).write();
                    this.db.set('product', dataObject.product).write();
                    this.db.set('order', dataObject.order).write();
                    this.db.set('updateDate', moment().valueOf()).write();
                    resolve();
                } else {
                    reject(new Error('canceled'));
                }
            })
        })
    }
    public importByKey(key: string) {
        const openOptions = {
            defaultPath: APP.getPath('documents') + '/data_' + key + 's.lowdb',
            filters: [
                { name: '数据库文件', extensions: ['lowdb'] }
            ]
        }
        return new Promise((resolve, reject) => {
            DIALOG.showOpenDialog(openOptions, (selectedPath) => {
                if (selectedPath) {
                    let data = fs.readFileSync(selectedPath[0], 'utf-8');
                    let dataObject = parse(data);
                    if (dataObject[key]) {
                        this.db.set(key, dataObject[key]).write();
                        resolve();
                    } else {
                        reject(new Error('没有相关数据'));
                    }
                } else {
                    reject(new Error('canceled'));
                }
            })
        })
    }
    public mergeData() {
        const openOptions = {
            defaultPath: APP.getPath('documents') + '/data_all.lowdb',
            filters: [
                { name: '数据库文件', extensions: ['lowdb'] }
            ]
        }
        return new Promise((resolve, reject) => {
            DIALOG.showOpenDialog(openOptions, (selectedPath) => {
                if (selectedPath) {
                    let data = fs.readFileSync(selectedPath[0], 'utf-8');
                    let dataObject = parse(data);
                    for (const key of Object.keys(dataObject)) {
                        if (key === 'updateDate') {
                            continue;
                        }
                        let current = this.db.get(key).value();
                        let incoming = dataObject[key];
                        this.db.set(key, _.merge(current, incoming)).write();
                    }
                    resolve();
                } else {
                    reject(new Error('canceled'));
                }
            })
        })
    }
    public mergeByKey(key: string) {
        const openOptions = {
            defaultPath: APP.getPath('documents') + '/data_' + key + 's.lowdb',
            filters: [
                { name: '数据库文件', extensions: ['lowdb'] }
            ]
        }
        return new Promise((resolve, reject) => {
            DIALOG.showOpenDialog(openOptions, (selectedPath) => {
                if (selectedPath) {
                    let data = fs.readFileSync(selectedPath[0], 'utf-8');
                    let dataObject = parse(data);
                    let current = this.db.get(key).value();
                    let incoming = dataObject[key];
                    this.db.set(key, _.merge(current, incoming)).write();
                    resolve();
                } else {
                    reject(new Error('canceled'));
                }
            })
        })
    }
    public restartAutoSaver() {
        this.stopAutoSaver();
        this.startAutoSaver();
    }
    public slientSave(configPath: string, hasFtp: boolean = false) {
        return new Promise((resolve, reject) => {
            if (configPath) {
                try {
                    let jsonString = stringify(this.db.value());
                    let fullpath = configPath + '\\autosave_all_' + moment().format('YYYYMMDDHHmmss') + '.lowdb';
                    fs.writeFile(fullpath, jsonString, 'utf-8', err => {
                        if (err) {
                            reject(err);
                        } else {
                            resolve();
                        }
                    })
                } catch (err) {
                    reject(err);
                }
            }
        })
    }
    public saveToFtp() {
        return new Promise((resolve, reject) => {
            try {
                const jsonString = stringify(this.db.value());
                const buffer = Buffer.from(jsonString, 'binary');
                const ftpUtil = new FtpUtil(this.getFtpConfig());
                ftpUtil.saveToFtp(buffer).then(() => {
                    resolve();
                }).catch(reason => {
                    reject(reason);
                });
            } catch (err) {
                reject(err);
            }
        })
    }
    public testFtpConnection() {
        return new Promise((resolve, reject) => {
            try {
                const ftpUtil = new FtpUtil(this.getFtpConfig());
                ftpUtil.testConnection().then(() => {
                    resolve();
                }).catch(reason => {
                    reject(reason);
                })
            } catch (err) {
                reject(err);
            }
        })
    }
    public saveOnExit() {
        return new Promise((resolve, reject) => {
            let autoSaveConfig = this.db.get('autoSaveConfig').value();
            if (autoSaveConfig) {
                if (autoSaveConfig.savePath) {
                    try {
                        let jsonString = stringify(this.db.value());
                        let timeStr = moment().format('YYYYMMDDHHmmss');
                        let fullpath = autoSaveConfig.savePath + '\\exit_all_' + timeStr + '.lowdb';
                        fs.writeFile(fullpath, jsonString, 'utf-8', err => {
                            if (err) {
                                reject(err);
                            } else {
                                resolve();
                            }
                        })
                    } catch (err) {
                        reject(err);
                    }
                }
            }
        })
    }
    public setAutoSaverConfig(config: AutoSaverConfig) {
        this.db.set('autoSaveConfig', config).write();
    }
    public setFtpConfig(config: FtpConfig) {
        this.db.set('ftpConfig', config).write();
    }
    public getAutoSaverConfig() {
        let result = this.db.get('autoSaveConfig').value();
        let config = new AutoSaverConfig(result);
        return config
    }
    public getFtpConfig() {
        let result = this.db.get('ftpConfig').value();
        let config = new FtpConfig(result);
        return config;
    }
    public startAutoSaver() {
        let dbAutoSaveConfig = this.db.get('autoSaveConfig').value();
        let config = new AutoSaverConfig(dbAutoSaveConfig);
        if (dbAutoSaveConfig && dbAutoSaveConfig.enable) {
            this.autoSaver = new AutoSaver(config);
        }
    }
    public stopAutoSaver() {
        if (this.autoSaver && this.autoSaver.running) {
            this.autoSaver.stop();
            this.autoSaver = null;
        }
    }
    public selectAutoSavePath() {
        let dbAutoSaveConfig = this.db.get('autoSaveConfig').value();
        let config = new AutoSaverConfig(dbAutoSaveConfig);
        return new Promise((resolve, reject) => {
            DIALOG.showOpenDialog({
                defaultPath: config.savePath,
                properties: ['openDirectory']
            }, (selectedPath) => {
                if (selectedPath) {
                    resolve(selectedPath[0]);
                } else {
                    reject(new Error('canceled'));
                }
            })
        })
    }
    public importImageToLocal() {
        return new Promise((resolve, reject) => {
            DIALOG.showOpenDialog({
                defaultPath: this.lastImageSelectedPath === '' ? APP.getPath('desktop') : this.lastImageSelectedPath,
                filters: [
                    { name: '图片文件', extensions: ['jpg', 'jpeg', 'png'] },
                    { name: '文档', extensions: ['doc', 'docx', 'pdf'] }
                ],
                properties: ['openFile', 'multiSelections']
            }, (files) => {
                if (files) {
                    if (files.length > 0) {
                        this.lastImageSelectedPath = path.dirname(files[0]);
                        let result: string[] = [];
                        files.forEach(item => {
                            let fileName = path.parse(item).base;
                            let target = IMAGE_PATH + fileName;
                            try {
                                fs.copyFileSync(item, target);
                            } catch (err) {
                                return reject(err);
                            }
                            result.push(target);
                        })
                        resolve(result);
                    }
                } else {
                    return reject(new Error('canceled'));
                }
            })
        })
    }
    public deleteFile(file: string) {
        return new Promise((resolve, reject) => {
            fs.unlink(file, (err) => {
                if (err) {
                    reject(err);
                } else {
                    resolve();
                }
            })
        })
    }
    public exportXlsx() {
        return new Promise((resolve, reject) => {
            let userJson = JSON.parse(JSON.stringify(this.db.get('user').value()));
            let orderJson = JSON.parse(JSON.stringify(this.db.get('order').value()));
            let productJson = JSON.parse(JSON.stringify(this.db.get('product').value()));
            const workBook = utils.book_new();
            const userSheet = utils.json_to_sheet(userJson);
            const orderSheet = utils.json_to_sheet(orderJson);
            const productSheet = utils.json_to_sheet(productJson);
            utils.book_append_sheet(workBook, orderSheet, '订单表');
            utils.book_append_sheet(workBook, userSheet, '客户表');
            utils.book_append_sheet(workBook, productSheet, '产品表');
            const result = xlsx.write(workBook, {
                bookType: 'xlsx',
                type: 'buffer',
                compression: true
            })
            const saveOptions = {
                defaultPath: APP.getPath('documents') + '/data.xlsx'
            }
            DIALOG.showSaveDialog(saveOptions, (selectedPath) => {
                if (selectedPath) {
                    fs.writeFile(selectedPath, result, (err) => {
                        if (err) {
                            reject(err)
                        } else {
                            resolve();
                        }
                    })
                } else {
                    reject(new Error('canceled'));
                }
            })
        })
    }
}
const db = new MyStorage();
export default db